package com.keya.features.tool;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.UID;
import org.dcm4che3.net.*;
import org.dcm4che3.net.pdu.AAssociateRQ;
import org.dcm4che3.net.pdu.PresentationContext;
import org.dcm4che3.tool.common.CLIUtils;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.List;

/**
 * EchoSCU
 *
 * @author : xf.he
 * @version : 1.0 2023/11/2
 * @since : 1.0
 **/
@Slf4j
public class EchoSCU {
    private final Device device = new Device("echoscu");
    private final ApplicationEntity ae = new ApplicationEntity("ECHOSCU");
    private final Connection conn = new Connection();
    private final Connection remote = new Connection();
    private final AAssociateRQ rq = new AAssociateRQ();
    private final Attributes attrs = new Attributes();
    private Association as;

    public EchoSCU() {
        device.addConnection(conn);
        device.addApplicationEntity(ae);
        ae.addConnection(conn);
    }

    public void setTransferSyntaxes(String[] tss) {
        rq.addPresentationContext(
                new PresentationContext(1, UID.Verification,
                        UID.ImplicitVRLittleEndian));
        rq.addPresentationContext(
                new PresentationContext(3,
                        UID.InstanceAvailabilityNotification,
                        tss));
    }

    public int cEcho(DicomNetParams dicomNetParams) {
        try {
            ae.setAETitle(dicomNetParams.getCallingAE());
            device.setDeviceName(device.getDeviceName());
            CommandLine cl = parseComandLine(initArgs(dicomNetParams));
            CLIUtils.configureConnect(remote, rq, cl);
            CLIUtils.configureBind(conn, ae, cl);
            CLIUtils.configure(conn, cl);
            remote.setTlsProtocols(conn.getTlsProtocols());
            remote.setTlsCipherSuites(conn.getTlsCipherSuites());
            setTransferSyntaxes(CLIUtils.transferSyntaxesOf(cl));
            CLIUtils.addAttributes(attrs, cl.getOptionValues("s"));
            device.setExecutor(PacsExecutor.scheduledExecutorService);
            device.setScheduledExecutor(PacsExecutor.scheduledExecutorService);
            try {
                open();
                echo();
            } finally {
                close();
            }
        } catch (ParseException e) {
            log.error("ianscu: " + e.getMessage());
            return 1;
        } catch (Exception e) {
            log.error("ianscu: " + e.getMessage());
            e.printStackTrace();
            return 1;
        }
        return 0;
    }

    private static CommandLine parseComandLine(String[] args) throws ParseException {
        Options opts = new Options();
        CLIUtils.addTransferSyntaxOptions(opts);
        CLIUtils.addConnectOption(opts);
        CLIUtils.addBindOption(opts, "ECHOSCU");
        CLIUtils.addAEOptions(opts);
        CLIUtils.addSendTimeoutOption(opts);
        CLIUtils.addResponseTimeoutOption(opts);
        CLIUtils.addCommonOptions(opts);
        return CLIUtils.parseComandLine(args, opts, null, EchoSCU.class);
    }

    public void open() throws IOException, InterruptedException, IncompatibleConnectionException, GeneralSecurityException {
        as = ae.connect(conn, remote, rq);
    }

    public void close() throws IOException {
        if (as != null) {
            as.release();
        }
    }

    public void echo() throws IOException, InterruptedException {
        as.cecho().next();
    }

    private static String[] initArgs(DicomNetParams netParas) {
        List<String> list = new ArrayList<>();
        list.add("-c");
        list.add(netParas.getCalledAE() + "@" + netParas.getCalledIP() + ":" + netParas.getCalledPort());
        if (netParas.getTimeout() != null) {
            list.add("--connect-timeout");
            list.add(String.valueOf(netParas.getTimeout()));
        }
        return list.toArray(new String[0]);
    }
}
